package utils

import (
	"archive/zip"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"project-deploy-service/models"
	"strings"
)

type fileUtil struct {
}

var FileUtil fileUtil

func (fu *fileUtil) GetFilename(filePath string) string {
	return filepath.Base(filePath)
}

// RemoveFileExtension 去除后缀
func (fu *fileUtil) RemoveFileExtension(fileName string) string {
	ext := filepath.Ext(fileName)
	return strings.TrimSuffix(fileName, ext)
}

// BackupFile 重命名文件
func (fu *fileUtil) BackupFile(oldName string, newName string) (string, error) {
	dir := filepath.Dir(oldName)
	dir = filepath.Join(dir, "backup")
	err := os.MkdirAll(dir, 0755)
	if err != nil {
		fmt.Println("Error creating directory:", err)
		return "", err
	}
	ext := filepath.Ext(oldName)

	newPath := filepath.Join(dir, fu.RemoveFileExtension(newName)+ext)

	err = os.Rename(oldName, newPath)
	if err != nil {
		return "", fmt.Errorf("无法重命名文件：%w", err)
	}
	return newPath, nil
}

// SaveToFile 将内容保存到文件
func (fu *fileUtil) SaveToFile(targetPath string, c *gin.Context) error {
	// 获取文件
	file, _, err := c.Request.FormFile("file")
	if err != nil {
		c.String(http.StatusBadRequest, fmt.Sprintf("获取文件失败: %s", err.Error()))
		return err
	}
	defer func(file multipart.File) {
		err := file.Close()
		if err != nil {
			return
		}
	}(file)
	outFile, err := os.Create(targetPath)
	if err != nil {
		c.String(http.StatusInternalServerError, fmt.Sprintf("创建目标文件失败: %s", err.Error()))
		return err
	}
	defer func(outFile *os.File) {
		err := outFile.Close()
		if err != nil {
			return
		}
	}(outFile)

	// 将上传的文件内容写入目标文件
	if _, err := io.Copy(outFile, file); err != nil {
		c.String(http.StatusInternalServerError, fmt.Sprintf("保存文件失败: %s", err.Error()))
		return err
	}

	c.String(http.StatusOK, fmt.Sprintf("文件上传成功！"))
	return nil
}

// LoadConfig 加载配置
func (fu *fileUtil) LoadConfig() models.Config {
	var config models.Config

	file, err := os.Open("conf/config.json")
	if err != nil {
		fmt.Println("Error opening JSON file:", err)
	}
	defer func(file *os.File) {
		err := file.Close()
		if err != nil {
			return
		}
	}(file)
	bytes, err := io.ReadAll(file)
	if err != nil {
		fmt.Println("Error reading JSON file:", err)
	}

	if err := json.Unmarshal(bytes, &config); err != nil {
		fmt.Println("Error parsing JSON file:", err)
	}
	return config
}

func (fu *fileUtil) Write(Filepath string, historyList any) error {
	indent, err := json.MarshalIndent(historyList, "", " ")
	if err != nil {
		return err
	}
	file, err := os.Create(Filepath)
	if err != nil {
		fmt.Println("Error creating file:", err)
		return err
	}
	defer func(file *os.File) {
		err := file.Close()
		if err != nil {
			return
		}
	}(file)
	_, err = file.Write(indent)
	if err != nil {
		fmt.Println("Error writing JSON to file:", err)
		return err
	}
	return nil
}

// Unzip 解压缩将zip文件解压缩到指定的目录。
// 请注意，目标目录不需要指定尾随路径分隔符。
// 如果目标目录不存在，它将自动创建。
func (fu *fileUtil) Unzip(zipath, dir string) error {
	// Open zip file.
	reader, err := zip.OpenReader(zipath)
	if err != nil {
		return err
	}
	defer func(reader *zip.ReadCloser) {
		err := reader.Close()
		if err != nil {
			return
		}
	}(reader)
	for _, file := range reader.File {
		if err := unzipFile(file, dir); err != nil {
			return err
		}
	}
	return nil
}

func unzipFile(file *zip.File, dir string) error {
	// Prevent path traversal vulnerability.
	// Such as if the file name is "../../../path/to/file.txt" which will be cleaned to "path/to/file.txt".
	name := strings.TrimPrefix(filepath.Join(string(filepath.Separator), file.Name), string(filepath.Separator))
	filePath := path.Join(dir, name)

	// Create the directory of file.
	if file.FileInfo().IsDir() {
		if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
			return err
		}
		return nil
	}
	if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
		return err
	}

	// Open the file.
	r, err := file.Open()
	if err != nil {
		return err
	}
	defer func(r io.ReadCloser) {
		err := r.Close()
		if err != nil {
			return
		}
	}(r)

	// Create the file.
	w, err := os.Create(filePath)
	if err != nil {
		return err
	}
	defer func(w *os.File) {
		err := w.Close()
		if err != nil {
			return
		}
	}(w)

	// Save the decompressed file content.
	_, err = io.Copy(w, r)
	return err
}
